package com.small.sentinel.slots.statistic;

import com.small.common.core.spi.Spi;
import com.small.common.designPattern.behavioral.chainOfResponsibility.slotchain.AbstractLinkedProcessorSlot;
import com.small.sentinel.Constants;
import com.small.sentinel.Context;
import com.small.sentinel.EntryType;
import com.small.sentinel.node.DefaultNode;
import com.small.sentinel.node.Node;
import com.small.sentinel.slotchain.ResourceWrapper;
import com.small.sentinel.slots.SentinelResource;
import com.small.sentinel.slots.block.BlockException;
import com.small.sentinel.slots.block.PriorityWaitException;
import com.small.sentinel.util.TimeUtil;

@Spi(order = Constants.ORDER_STATISTIC_SLOT)
public class StatisticSlot extends AbstractLinkedProcessorSlot<SentinelResource>{
    @Override
    public void entry(SentinelResource resource) throws Throwable {
        Context context = resource.getContext();
        DefaultNode node = resource.getDefaultNode();
        ResourceWrapper resourceWrapper = resource.getResourceWrapper();

        int count = resource.getCount();

        try {
            // 1. 先执行后续Slot的entry
            fireEntry(resource);
            node.increaseThreadNum();
            node.addPassRequest(resource.getCount());

            // 2-3 全局入口流量统计
            if (resource.getResourceWrapper().getEntryType() == EntryType.IN) {
                // Add count for global inbound entry node for global statistics.
                Constants.ENTRY_NODE.increaseThreadNum();
                Constants.ENTRY_NODE.addPassRequest(resource.getCount());
            }
        //优先级等待异常
        } catch (PriorityWaitException ex) {
            node.increaseThreadNum();
            if (resource.getResourceWrapper().getEntryType() == EntryType.IN) {
                Constants.ENTRY_NODE.increaseThreadNum();
            }
        //限流异常
        } catch (BlockException e) {
            context.getCurEntry().setBlockError(e);
            node.increaseBlockQps(count);

            if (resourceWrapper.getEntryType() == EntryType.IN) {
                Constants.ENTRY_NODE.increaseBlockQps(count);
            }
            throw e;
        //限流以外的其他异常
        }catch (Throwable e) {
            context.getCurEntry().setError(e);
            throw e;
        }

    }

    @Override
    public void exit(SentinelResource resource) throws Throwable {
        Context context = resource.getContext();
        int count = resource.getCount();
        ResourceWrapper resourceWrapper = resource.getResourceWrapper();
        Node node = context.getCurNode();
        //没有发生校验异常
        if (context.getCurEntry().getBlockError() == null) {
            long completeStatTime = TimeUtil.currentTimeMillis();
            context.getCurEntry().setCompleteTimestamp(completeStatTime);
            //完成时长
            long rt = completeStatTime - context.getCurEntry().getCreateTimestamp();
            Throwable error = context.getCurEntry().getError();

            recordCompleteFor(node, count, rt, error);
            if (resourceWrapper.getEntryType() == EntryType.IN) {
                recordCompleteFor(Constants.ENTRY_NODE, count, rt, error);
            }
        }
        fireExit(resource);
    }

    /**
     * 记录完成时间
     * @param node
     * @param batchCount
     * @param rt
     * @param error
     */
    private void recordCompleteFor(Node node, int batchCount, long rt, Throwable error) {
        if (node == null) {
            return;
        }
        //记录完成时长
        node.addRtAndSuccess(rt, batchCount);
        //减少线程数
        node.decreaseThreadNum();

        //非校验异常
        if (error != null && !(error instanceof BlockException)) {
            node.increaseExceptionQps(batchCount);
        }
    }
}
